iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0
自我挑戰組

打掉重練!Django的還債之旅~系列 第 9

Day09. request加工廠"middleware"~

  • 分享至 

  • xImage
  •  

前言

上回我們接到了request但跳過了其中處理request的部分,而其中django在處理request的部分呢,就是middleware中間件,middleware會把收到的request處理後生成response,而middleware又有好幾個,他們是怎麼串連在一起的呢?具體又是怎麼做的呢?我們待會就來一探究竟~

正題

我們先回到上次抓到middleware的地方!
https://ithelp.ithome.com.tw/upload/images/20230924/20162905cYL5ROgF7G.png
其中會看到response = self._middleware_chain(request),這個middleware_chain怎麼形成的呢?來去看看~
點下去會看到是在BaseHandler中的function load_middleware()(太長了截圖不了,以下是他的原始碼

# BaseHandle
def load_middleware(self, is_async=False):
    """
    Populate middleware lists from settings.MIDDLEWARE.

    Must be called after the environment is fixed (see __call__ in subclasses).
    """
    self._view_middleware = []
    self._template_response_middleware = []
    self._exception_middleware = []

    get_response = self._get_response_async if is_async else self._get_response
    handler = convert_exception_to_response(get_response)
    handler_is_async = is_async
    for middleware_path in reversed(settings.MIDDLEWARE):
        middleware = import_string(middleware_path)
        middleware_can_sync = getattr(middleware, "sync_capable", True)
        middleware_can_async = getattr(middleware, "async_capable", False)
        if not middleware_can_sync and not middleware_can_async:
            raise RuntimeError(
                "Middleware %s must have at least one of "
                "sync_capable/async_capable set to True." % middleware_path
            )
        elif not handler_is_async and middleware_can_sync:
            middleware_is_async = False
        else:
            middleware_is_async = middleware_can_async
        try:
            # Adapt handler, if needed.
            adapted_handler = self.adapt_method_mode(
                middleware_is_async,
                handler,
                handler_is_async,
                debug=settings.DEBUG,
                name="middleware %s" % middleware_path,
            )
            mw_instance = middleware(adapted_handler)
        except MiddlewareNotUsed as exc:
            if settings.DEBUG:
                if str(exc):
                    logger.debug("MiddlewareNotUsed(%r): %s", middleware_path, exc)
                else:
                    logger.debug("MiddlewareNotUsed: %r", middleware_path)
            continue
        else:
            handler = adapted_handler

        if mw_instance is None:
            raise ImproperlyConfigured(
                "Middleware factory %s returned None." % middleware_path
            )

        if hasattr(mw_instance, "process_view"):
            self._view_middleware.insert(
                0,
                self.adapt_method_mode(is_async, mw_instance.process_view),
            )
        if hasattr(mw_instance, "process_template_response"):
            self._template_response_middleware.append(
                self.adapt_method_mode(
                    is_async, mw_instance.process_template_response
                ),
            )
        if hasattr(mw_instance, "process_exception"):
            # The exception-handling stack is still always synchronous for
            # now, so adapt that way.
            self._exception_middleware.append(
                self.adapt_method_mode(False, mw_instance.process_exception),
            )

        handler = convert_exception_to_response(mw_instance)
        handler_is_async = middleware_is_async

    # Adapt the top of the stack, if needed.
    handler = self.adapt_method_mode(is_async, handler, handler_is_async)
    # We only assign to this when initialization is complete as it is used
    # as a flag for initialization being complete.
    self._middleware_chain = handler

在最後一行可以看到我們的目標self._middleware_chain = handler,在來回推看看這個handler怎麼形成的吧~
先把焦點移至上面
https://ithelp.ithome.com.tw/upload/images/20230924/20162905fmyQIoVCGH.png
會看到他把self._get_response丟進去一個function convert_exception_to_response()裡面後指定給變數handler,我們先來看看生成handler的這支function
https://ithelp.ithome.com.tw/upload/images/20230924/20162905BLQyvP4ich.png
這個function其實看function name大概就可以猜測他想做什麼了,而看註解後就更詳細了~
我們也可以從程式碼中看到django會去處理sync or async,而預設是sync所以我們就看sync的部分就好了~
https://ithelp.ithome.com.tw/upload/images/20230924/201629054Mrv0cBfYq.png
這個部分是python自己寫decorator的做法,因為我自己也沒什麼客製化寫過decorator所以看的時候花了一些時間去找出程式的流向!這邊可以看到他把傳進來的get_response參數丟到inner裡面後去處理try catch吐出結果response,然而這些都會在實際那個self._middleware_chain被call的時候才會觸發,這邊會拋出inner這個function出去到外面那個handler,說得有點抽象,畫個圖來看看~
https://ithelp.ithome.com.tw/upload/images/20230924/20162905tQ48Tkm4K3.png
應該還算清楚XD
那現在我們知道了handler會是一個inner function包含了處理request的邏輯,那我們繼續往下看那個for迴圈
https://ithelp.ithome.com.tw/upload/images/20230924/201629052fVLA1lhg8.png
可以看到他會去跑一遍settings.MIDDLEWARE也就是這個,而且是反轉的(這邊之後再講)
https://ithelp.ithome.com.tw/upload/images/20230924/20162905UV7Ni3j8on.png
把該middleware import進來後,會把原先的handler由self.adapt_method_mode()去check並轉換sync or async,最後再把他丟進去前面import進來的middleware -> mw_instance = middleware(handler)
之後的過程處理一些middleware中的function(這邊我們也之後再說)
https://ithelp.ithome.com.tw/upload/images/20230924/20162905Xz8b5iiNmn.png
最後來到這裡,for loop的尾巴
https://ithelp.ithome.com.tw/upload/images/20230924/20162905TzoR5VcdA4.png
又再把剛剛的import middleware丟給handler = convert_exception_to_response(mw_instance),那我們前面已經知道這個handler是一個inner function了,就這樣把for loop跑完後指定給self._middleware_chain
https://ithelp.ithome.com.tw/upload/images/20230924/20162905xInxBlC2VP.png
最後再來畫一張圖看看~
https://ithelp.ithome.com.tw/upload/images/20230924/20162905J5di0aoFJb.png

結語

我們今天看了middleware_chain生成的過程,但跳過了兩個重要的地方

  • self._get_response
  • Middleware這個class本身

這兩個過明後天再來看看吧!!
/images/emoticon/emoticon12.gif


上一篇
Day08. Request來了 ! 我接!
下一篇
Day10. middleware mixin
系列文
打掉重練!Django的還債之旅~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言